/**
 * HTTP请求封装（最终版）
 * 功能：队列管理、Token自动刷新、错误处理
 * @author will
 * @date 2023/08/24
 */

import appConfig from '@/config/appConfig.js'
import storageManage from '@/util/storageManage.js'
import { sm4EncryptByReqData, sm4DecryptByResData } from '@/util/encryptUtil.js'
import $infoBox from '@/common/fun.js'

// 全局请求队列
class RequestQueue {
  constructor() {
    this.queue = []          // 普通队列
    this.priorityQueue = []  // 优先级队列
    this.isProcessing = false
    this.refreshPromise = null // Token刷新Promise
  }

  enqueue(taskFn, options = {}) {
    return new Promise((resolve, reject) => {
      const task = {
        taskFn,
        resolve,
        reject,
        isPriority: options.isPriority || false,
        retry: options.retry || 0
      }

      // 插入队列
      options.isPriority 
        ? this.priorityQueue.unshift(task) // 高优先级插队
        : this.queue.push(task)
		
      if (!this.isProcessing) this.process()
    })
  }

  async process() {
    this.isProcessing = true
    while (this.priorityQueue.length > 0 || this.queue.length > 0) {
      const task = this.priorityQueue.shift() || this.queue.shift()
      
      try {
        const result = await task.taskFn()
        task.resolve(result)
      } catch (error) {
        // if (task.retry < 2) { // 自动重试2次
        //   task.retry++
        //   this.priorityQueue.unshift(task)
        // } else {
        //   task.reject(error)
        // }
		task.reject(error)
      }
    }
	this.isProcessing = false
  }
}

const requestQueue = new RequestQueue()
const UNENCRYPTED_URIS = ['/api/fans/getWxchatPayOpenid', '/api/fans/getPlaceWxchatOpenid', '/api/sms/sendMsg']

// Token刷新锁
let isRefreshing = false

// 核心请求方法
async function coreRequest(uri, data, method, extParams, showLoading = true) {
  /* if(showLoading){
	uni.showLoading({
		title:"加载中..."
	})
  } */
  // 构建请求配置
  const headers = {
    'Access-Control-Allow-Origin': '*',
    'appid': appConfig.appId,
	'belongAgentNo': appConfig.belongAgentNo,
	'locationAgentNo': storageManage.agentInfo().agentNo,
    // 'officialAppId': appConfig.officialAppId,
    ...(storageManage.token() && { [appConfig.tokenKey]: storageManage.token() })
  }
  
  if(storageManage.version() !== void 0){
	  headers.version = storageManage.version()
  }
  
  if(method === 'GET'){	// 解决后端GET请求无法解析问题
	headers['Content-Type'] = 'charset=UTF-8'
  }

  const shouldEncrypt = method !== 'GET' && storageManage.dataKey() && !UNENCRYPTED_URIS.includes(uri)
  
  const config = {
    url: appConfig.env.GZ_BASE_URL + uri,
    method: method.toUpperCase(),
    header: headers,
    // data: shouldEncrypt ? sm4EncryptByReqData(data) : data,
	data:data,
    timeout: 16000,
    ...extParams
  }

  try {
    const response = await uni.request(config)
    return await processResponse(response, { uri, data, method, extParams })
  } catch (error) {
    return handleRequestError(error)
  }
}
// 处理请求错误
function handleRequestError(error) {
  uni.hideLoading()

  const errorMessage = error.errMsg || error.message || '请求异常'
  
  if (errorMessage && errorMessage?.includes('request:fail')){
	// 网络异常 跳转网络维护页面
	uni.reLaunch({
		url: '/pages/networkError/index'
	})
	return Promise.reject(errorMessage)
  }
  
  if (errorMessage.includes('timeout') || errorMessage.includes('TIMEOUT')) {
    $infoBox.toast('系统接口请求超时，请稍后重试！')
  } else {
    $infoBox.toast(errorMessage)
  }
  
  return Promise.reject(errorMessage)
}

// 响应处理器
async function processResponse(response, originalRequest) {
  const { statusCode, data: bodyData = {} } = response
  
  // 特殊错误处理
  // Token过期处理（401逻辑）
  if (statusCode === 401 || bodyData.code === 401) {
	return await refreshToken(originalRequest)
  }
  // 40001用户信息未完善
  if (bodyData.code == 40001){
	uni.showModal({
		title: '提示',
		content: bodyData.msg,
		success: ({ confirm }) => {
			confirm && uni.navigateTo({ url: '/pageUser/fansInfo/index' })
		}
	})
	throw new Error('请先完善用户信息')
  }
  // 40002错误处理
  if (bodyData.code == 40002) { // 40002 参与活动 请先关注活动公众号 msg
	uni.showModal({
		title: '提示',
		content: '请先关注活动公众号',
		success({ cancel }) {
			if (cancel) return
			if (bodyData.msg){
				uni.navigateTo({
					url: `/pages/fllowOfficialAccount/fllowOfficialAccount?qrcodeImg=${bodyData.msg}`
				})
			}
		}
	})
	return { code: 40002 }
  }
  /**
   * specialCodes 返回code 交给具体页面进行处理
   * 40004抢券中状态
   * 40005 用户支付未授权 涉及接口(扫码派券\)
   * 40006错误处理 提示用户重复兑换卡券
   */
  const specialCodes = [40004,40005,40006]
  if(specialCodes.includes(bodyData.code)){
  	return { bizData: bodyData.msg, code: bodyData.code }
  }
  // 其他错误处理
  if (statusCode !== 200) throw new Error(`HTTP错误: ${statusCode}`)
  if (bodyData.code !== 0) throw new Error(bodyData.msg || '未知错误')
  
  let bizData = null
  let dynamicKey = null
  if(bodyData.encryptData){
  	  const { data, dynamicKey:decryDynamicKey } = sm4DecryptByResData(bodyData.encryptData)
	  if(!decryDynamicKey){	// 兼容旧后台写法 , 下一版本可删除该判断
		bizData = sm4DecryptByResData(bodyData.encryptData)
		dynamicKey = {}
	  }else{
		bizData = data
		dynamicKey = decryDynamicKey || {}
	  }
  }else{
  	  bizData = bodyData.data
  	  dynamicKey = bodyData.data?.dynamicKey || {}
  }
  
  const { dataKey, version } = dynamicKey
  // 密钥版本号处理
  if(dataKey && version !== void 0){
	  storageManage.dataKey(dataKey)
  	  storageManage.version(version)
  }
  /* uni.hideLoading() */
  // 解密处理
  return {
    bizData,
    code: bodyData.code
  }
}

// Token刷新方法
async function refreshToken(originalRequest) {
  storageManage.token('',true)
  const pages = getCurrentPages()
  const currentPage = pages[pages.length - 1]?.$page?.fullPath || ''
  if(!storageManage.fansId() || !storageManage.payOpenId()){
	setTimeout(() => {
		uni.reLaunch({
			url: `/pages/welcome/index?redirect_uri=${encodeURIComponent(currentPage)}`
		})
	},1500)
	$infoBox.toast("身份过期，重新登陆中！")
  	return Promise.reject("身份过期，重新登陆中！")
  }
  try {
    const { code } = await uni.login()
    const payload = {
      wxcode: code,
      appId: appConfig.appId,
      agentNo: appConfig.agentNo,
      fansId: storageManage.fansId(),
      payOpenId: storageManage.payOpenId(),
      inviterType: 0
    }
    // 发送高优先级请求
	const { data:resData } = await uni.request({
		url: appConfig.env.GZ_BASE_URL + '/api/fans/getPlaceWxchatOpenid',
		data: payload,
		method: 'POST',
		header: {
			'Access-Control-Allow-Origin': '*',
			'appid': appConfig.appId,
			'belongAgentNo': appConfig.belongAgentNo,
			'locationAgentNo': storageManage.agentInfo().agentNo,
			// 'officialAppId': appConfig.officialAppId
		}
	})
	const { token, userId, isLogin, isBind, placeOpenId, placeAppid, openId:liteOpenId, dynamicKey, dataKey,state } = resData.data
		if(state==1||state==2){
			storageManage.isLogin(false)
			storageManage.userInfo(undefined,true)
			storageManage.token('',true)
			storageManage.dataKey('',true)
			storageManage.version('',true)
			uni.clearStorageSync()
			uni.createCacheManager().clearCaches()
		uni.reLaunch({
			url: '/pages/welcome/index'
		})
			return false
		}
	storageManage.dataKey(dynamicKey?.dataKey)
	storageManage.version(dynamicKey?.version)
	if(dataKey){	// 兼容老后台 下一版本可删除该dataKey的赋值
		storageManage.dataKey(dataKey)
	}
    storageManage.token(token)
    storageManage.userId(userId)
    storageManage.isLogin(!!isLogin)
    storageManage.isBind(!!isBind)
    storageManage.liteOpenId(liteOpenId)	// 用户小程序openId
    storageManage.placeOpenId(placeOpenId)
	const { uri, data, method, extParams } = originalRequest
	return await coreRequest(uri, data, method, extParams)
  } catch (error) {
    console.error('Token刷新失败:', error)
    throw error
  }
}

// 业务错误处理 reject
function handleBusinessError(bodyData) {
  
}


// 统一导出方法
export const http = {
  req: (uri, params, method) => requestQueue.enqueue(() => coreRequest(uri, params, method)),
  get: (uri, params) => requestQueue.enqueue(() => coreRequest(uri, params, 'GET')),
  post: (uri, data, showLoading) => requestQueue.enqueue(() => coreRequest(uri, data, 'POST', {}, showLoading)),
  add: (uri, data) => requestQueue.enqueue(() => coreRequest(uri, data, 'POST')),
  put: (uri, data) => requestQueue.enqueue(() => coreRequest(uri, data, 'PUT')),
  delete: (uri) => requestQueue.enqueue(() => coreRequest(uri, {}, 'DELETE')),
  list: (uri, params) => requestQueue.enqueue(() => coreRequest(uri, params, 'GET')),
  getById: (uri, id) => requestQueue.enqueue(() => coreRequest(`${uri}/${id}`, {}, 'GET')),
  updateById: (uri, id, data) => requestQueue.enqueue(() => coreRequest(`${uri}/${id}`, data, 'PUT')),
  deleteById: (uri, id) => requestQueue.enqueue(() => coreRequest(`${uri}/${id}`, {}, 'DELETE'))
}